<?php

namespace Common\Common;

use Think\Model;

class AppVerify
{
    public static $appId  = '';			   // 应用编号
    public static $appKey = '';			   // 应用密钥
    public static $appIp  = '';			   // 应用IP

    public static function check()
    {
        if (!self::initApp() || !self::checkSign())
        {
            //todo
            //return false;
        }
        return true;
    }

    // 初始化应用
    private static function initApp()
    {
        $appId = $_REQUEST['api_appid'];
        $appModel = D('Applications');
        $sqlWhere = null;
        $sqlWhere['app_id'] = $appId;
        $app = $appModel->where($sqlWhere)->find();
        if (!$app)
        {
            ReturnParams::$code = 'invalid-item:app:not-exist';
            return false;
        }
        // 检查app状态
        if ($app['status'] != 'normal')
        {
            ReturnParams::$code = 'invalid-item:app:disabled ';
            return false;
        }

        self::$appId       = $appId;
        self::$appKey      = $app['app_key'];
        self::$appIp       = $_SERVER['REMOTE_ADDR'];

        return true;
    }

    // 检查参数签名
    private static function checkSign()
    {
        if (C('ENVIRONMENT') == 'development')
        {
            return true;
        }

        $api_timestamp = $_REQUEST['api_timestamp'];
        $now = time();

        // 时间戳验证，允许15分钟的误差
        if ($api_timestamp < $now-C('TIMESTAMP_EXPIRATION') || $api_timestamp > $now+900)
        {
            ReturnParams::$code = 'invalid-signature:api_timestamp:verify-failed';
            return false;
        }

        if (!self::checkSignNonce())
        {
            return false;
        }

        $api_signature  = $_REQUEST['api_signature'];
        $params = $_REQUEST;
        unset($params['api_signature']);

        // 生成签名
        $signature = self::getSign($params, self::$appKey);
        if (!$signature) return false;

        if ($api_signature != $signature)
        {
            ReturnParams::$code = 'invalid-signature:api_signature:verify-failed';
            return false;
        }
        return true;
    }

    // 检查随机数
    private static function checkSignNonce()
    {
        $api_sign_nonce = $_REQUEST['api_sign_nonce'];
        if (!$api_sign_nonce)
        {
            ReturnParams::$code = 'invalid-signature:api_sign_nonce:missing';
            return false;
        }

        $now = time();
        $nonceModel = M('Nonces');
        if (rand(1,1000) == 1)
        {
            $sqlWhere                = null;
            // 时间戳的有效时间为 C('TIMESTAMP_EXPIRATION') + 15分钟 ，所以清除表中过期记录的应小于这个值
            $sqlWhere['create_time'] = array('lt', $now-C('TIMESTAMP_EXPIRATION')-3600);
            $nonceModel->where($sqlWhere)->delete();
        }
        $sqlWhere                = null;
        $sqlWhere['code']        = $api_sign_nonce;
        $sqlWhere['create_time'] = array('egt', $now-C('TIMESTAMP_EXPIRATION'));
        if ($nonceModel->where($sqlWhere)->find())
        {
            ReturnParams::$code = 'invalid-signature:api_sign_nonce:verify-failed';
            return false;
        }
        else
        {
            $sqlData                = null;
            $sqlData['code']        = $api_sign_nonce;
            $sqlData['create_time'] = $now;
            $nonceModel->create($sqlData, Model::MODEL_INSERT);
            $nonceModel->add();
            return true;
        }
    }

    // 生成签名串
    public static function getSign($params, $appKey)
    {
        // 将请求参数中非全局参数名称中的下划线替换回.（API的Action参数名不可能包含下划线）
        foreach ($params as $key=>$value)
        {
            if (in_array($key, array('api_appid', 'api_format', 'api_timestamp', 'api_sign_nonce',
                'api_signature', 'api_action', 'api_callback', )))
                continue;

            if (strpos($key, '_') == false) continue;

            $key_replaced = str_replace('_', '.', $key);
            $params[$key_replaced] = $value;
            unset($params[$key]);
        }
        unset($params['_url']);
        ksort($params);
        $paramStr = http_build_query($params);
        return self::sign($paramStr, $appKey);
    }

    private static function sign($data, $key)
    {
        return base64_encode(hash_hmac('sha1', $data, $key, true));
    }

}